package org.dbstar.scheduler.job;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 * Job的状态
 * 
 * @author 代波
 * @since 1.0.0
 * @version 1.0.0
 * @date 2012-3-23
 */
public enum JobStatus {
	/**
	 * <pre>
	 * init：Job初始化
	 * submit：Job提交到jobController
	 * ready：前置条件满足，可以执行
	 * error：任务无法执行
	 * queue：加入执行队列
	 * pause：暂停执行
	 * cancel：Job未执行前被取消
	 * run：开始执行
	 * finish：执行过程正常结束
	 * kill：执行过程中被取消
	 * retry：执行过程等待重试
	 * </pre>
	 */
	init, submit, ready, error, queue, pause, cancel, run, finish, kill, retry;

	public static final Set<JobStatusChange> CHANGE_SET = buildChangeSet();

	// 有效的状态转换
	private static Set<JobStatusChange> buildChangeSet() {
		Set<JobStatusChange> set = new HashSet<JobStatus.JobStatusChange>();
		set.add(change(init, submit));// DefaultJobSchedulerExecutor.submit(), CompoundJobRunner.run()
		set.add(change(init, cancel));// DefaultJobSchedulerExecutor.cancel(), CompoundJobRunner.run()
		set.add(change(submit, ready));// DefaultJobSchedulerExecutor.checkReady()
		set.add(change(ready, error));// DefaultJobSchedulerExecutor.run()
		set.add(change(ready, queue));// DefaultJobSchedulerExecutor.run()
		set.add(change(ready, pause));// DefaultJobSchedulerExecutor.pause()
		set.add(change(ready, cancel));// DefaultJobSchedulerExecutor.cancel()
		set.add(change(queue, pause));// DefaultJobSchedulerExecutor.pause()
		set.add(change(error, init));// CompoundJobResult.reExecute()
		set.add(change(error, submit));// RunJobConfAction.execute()
		set.add(change(queue, ready));// DefaultJobSchedulerExecutor.start(), DefaultJobSchedulerExecutor.shutdown()
		set.add(change(queue, cancel));// DefaultJobSchedulerExecutor.cancel()
		set.add(change(queue, run));// AbstractJobRunner.call()
		set.add(change(pause, ready));// DefaultJobSchedulerExecutor.resume()
		set.add(change(pause, cancel));// DefaultJobSchedulerExecutor.cancel()
		set.add(change(cancel, init));// CompoundJobResult.reExecute()
		set.add(change(cancel, submit));// RunJobConfAction.execute()
		set.add(change(run, ready));// DefaultJobSchedulerExecutor.start(), DefaultJobSchedulerExecutor.shutdown()
		set.add(change(run, error));// AbstractJobRunner.call()
		set.add(change(run, finish));// AbstractJobRunner.call()
		set.add(change(run, kill));// AbstractJobRunner.call()
		set.add(change(finish, init));// CompoundJobResult.reExecute()
		set.add(change(finish, submit));// RunJobConfAction.execute()
		set.add(change(finish, retry));// CacheableJobController.onJobStatusChange()
		set.add(change(kill, init));// CompoundJobResult.reExecute()
		set.add(change(kill, submit));// RunJobConfAction.execute()
		set.add(change(retry, init));// CompoundJobResult.reExecute()
		set.add(change(retry, ready));// RetryJob.execute()
		set.add(change(retry, submit));// RunJobConfAction.execute()
		return Collections.unmodifiableSet(set);
	}

	public static final boolean isValidChange(JobStatus from, JobStatus to) {
		return CHANGE_SET.contains(change(from, to));
	}

	public static final JobStatusChange change(JobStatus from, JobStatus to) {
		return new JobStatusChange(from, to);
	}

	public static final class JobStatusChange {
		public final JobStatus from;
		public final JobStatus to;

		private JobStatusChange(JobStatus from, JobStatus to) {
			this.from = from;
			this.to = to;
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((from == null) ? 0 : from.hashCode());
			result = prime * result + ((to == null) ? 0 : to.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			JobStatusChange other = (JobStatusChange) obj;
			if (from != other.from)
				return false;
			if (to != other.to)
				return false;
			return true;
		}
	}
}
